﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConfigurationDemo
{
    //基于命令行的配置扩展方法
    public static class CommandLineConfigurationExtensions
    {
        public static IConfigurationBuilder AddCommandLine(this IConfigurationBuilder configurationBuilder, string[] args)
        {
            return configurationBuilder.AddCommandLine(args, switchMappings: null);
        }

        public static IConfigurationBuilder AddCommandLine(
            this IConfigurationBuilder configurationBuilder,
            string[] args,
            IDictionary<string, string>? switchMappings)
        {
            configurationBuilder.Add(new CommandLineConfigurationSource { Args = args, SwitchMappings = switchMappings });
            return configurationBuilder;
        }

        public static IConfigurationBuilder AddCommandLine(this IConfigurationBuilder builder, Action<CommandLineConfigurationSource>? configureSource)
            => builder.Add(configureSource);
    }

    //基于命令行的配置源
    public class CommandLineConfigurationSource : IConfigurationSource
    {
        public IDictionary<string, string>? SwitchMappings { get; set; }
        public IEnumerable<string> Args { get; set; } = Array.Empty<string>();

        public IConfigurationProvider Build(IConfigurationBuilder builder)
        {
            return new CommandLineConfigurationProvider(Args, SwitchMappings);
        }
    }

    //基于命令行的配置提供程序 为了兼容Linux和Windows的命令行参数格式
    //-k1=value1 -k2 value2 --alt3=value2 /alt4=value3 --alt5 value5 /alt6 value6 person=caoruipeng
    public class CommandLineConfigurationProvider : ConfigurationProvider
    {
        /*
         2. 为什么需要 _switchMappings
            _switchMappings 的作用是将命令行参数的别名映射到标准化的配置键，从而提供更高的灵活性和可读性。
主要用途：
1.	支持别名：
•	用户可能希望使用更短的命令行参数（例如 -k）来代替完整的配置键（例如 KeyName）。
•	_switchMappings 提供了这种映射能力，使得用户输入的参数可以被转换为标准化的配置键。
2.	提高可读性：
•	命令行参数通常较短且不直观，例如 -k1，通过 _switchMappings 可以将其映射为更具语义的键（例如 Database:ConnectionString）。
3.	兼容性：
•	不同的命令行工具可能使用不同的参数名称，通过 _switchMappings，可以将这些名称统一映射到应用程序的配置键。
         
         */
        private readonly Dictionary<string, string>? _switchMappings;//负责将指定的key映射到配置的key

        public CommandLineConfigurationProvider(IEnumerable<string> args, IDictionary<string, string>? switchMappings = null)
        {
            Args = args;
            if (switchMappings != null)
            {
                _switchMappings = GetValidatedSwitchMappingsCopy(switchMappings);
            }
        }

        //所有命令行参数
        protected IEnumerable<string> Args { get; }

        public override void Load()
        {
            //以空格区分的每个参数 都可以解析出一个key-value键值对
            var data = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
            string key, value;
            //注意/开头的命令行参数会被转换成--开头的命令行参数  也就是说 /name=1 和--name=1 是等价的
            using (IEnumerator<string> enumerator = Args.GetEnumerator())
            {
                //遍历所有命令行参数
                while (enumerator.MoveNext())
                {
                    string currentArg = enumerator.Current;
                    //计算Key的起始索引
                    int keyStartIndex = 0;
                    if (currentArg.StartsWith("--"))
                    {
                        keyStartIndex = 2;
                    }
                    else if (currentArg.StartsWith("-"))
                    {
                        keyStartIndex = 1;
                    }
                    else if (currentArg.StartsWith("/"))
                    {
                        currentArg = $"--{currentArg.Substring(1)}";
                        keyStartIndex = 2;
                    }

                    //开始解析value
                    int separator = currentArg.IndexOf('=');

                    if (separator < 0)//当前命令行参数没有=
                    {
                        // If there is neither equal sign nor prefix in current argument, it is an invalid format
                        if (keyStartIndex == 0)
                        {
                            // Ignore invalid formats
                            continue;
                        }

                        // If the switch is a key in given switch mappings, interpret it
                        if (_switchMappings != null && _switchMappings.TryGetValue(currentArg, out string? mappedKey))
                        {
                            key = mappedKey;
                        }
                        // If the switch starts with a single "-" and it isn't in given mappings , it is an invalid usage so ignore it
                        else if (keyStartIndex == 1)
                        {
                            continue;
                        }
                        // Otherwise, use the switch name directly as a key
                        else
                        {
                            key = currentArg.Substring(keyStartIndex);
                        }
                        //因为没有= 所有要移动到下一个参数作为value
                        if (!enumerator.MoveNext())
                        {
                            // ignore missing values
                            continue;
                        }

                        value = enumerator.Current;
                    }
                    else//当前命令行参数有=
                    {
                        //直接获取命令行参数的key
                        string keySegment = currentArg.Substring(0, separator);

                        if (_switchMappings != null && _switchMappings.TryGetValue(keySegment, out string? mappedKeySegment))
                        {
                            key = mappedKeySegment;
                        }
                        // If the switch starts with a single "-" and it isn't in given mappings , it is an invalid usage
                        else if (keyStartIndex == 1)
                        {
                            throw new FormatException("SR.Format(SR.Error_ShortSwitchNotDefined, currentArg)");
                        }
                        // Otherwise, use the switch name directly as a key
                        else
                        {
                            //字符串截取key
                            key = currentArg.Substring(keyStartIndex, separator - keyStartIndex);
                        }
                        //字符串截取value
                        value = currentArg.Substring(separator + 1);
                    }

                    // Override value when key is duplicated. So we always have the last argument win.
                    data[key] = value;
                }
            }

            Data = data;
        }

        private static Dictionary<string, string> GetValidatedSwitchMappingsCopy(IDictionary<string, string> switchMappings)
        {
            // The dictionary passed in might be constructed with a case-sensitive comparer
            // However, the keys in configuration providers are all case-insensitive
            // So we check whether the given switch mappings contain duplicated keys with case-insensitive comparer
            var switchMappingsCopy = new Dictionary<string, string>(switchMappings.Count, StringComparer.OrdinalIgnoreCase);
            foreach (KeyValuePair<string, string> mapping in switchMappings)
            {
                // Only keys start with "--" or "-" are acceptable
                if (!mapping.Key.StartsWith("-") && !mapping.Key.StartsWith("--"))
                {
                    throw new ArgumentException( "SR.Format(SR.Error_InvalidSwitchMapping, mapping.Key),nameof(switchMappings));");
                }

                if (switchMappingsCopy.ContainsKey(mapping.Key))
                {
                    throw new ArgumentException();
                }

                switchMappingsCopy.Add(mapping.Key, mapping.Value);
            }

            return switchMappingsCopy;
        }
    }
}
